use strict;
use warnings;
use Data::Dumper;

system $^O eq 'MSWin32' ? 'cls' : 'clear';

# Rutas de archivos y directorios necesarios
my $cd 					= $ENV{'GRUPO'};
my $maedir				= $ENV{'MAEDIR'};
my $repodir				= $ENV{'REPODIR'};
if (!($cd && $maedir && $repodir)) {
	print "Error: Variables de entorno necesarias no establecidas\n";
	exit;
}
my $archivoSucursales 	= "$maedir/sucu.mae";
my $archivoClientes 	= "$maedir/cli.mae";
my $dirGrabarParque		= "$cd/parque_instalado";
my $grabarReporte 		= 0;
my $mostrarReporte		= 0;

# Declaracion de hashes
if (@ARGV == 0) {
	print "Error de sintaxis: Cantidad de parametros incorrecta\n";
	exit;
}

# Filtrado de caracteres no permitidos
foreach my $varAux (@ARGV) {
	if (!$varAux =~ m/^([0-9]|[A-Z]|[,]|[-])+$/) {
		print "Error de sintaxis: Caracter no valido\n";
		exit;
	}
}

my $modo = uc $ARGV[0];
my %data = ();
my %reporte = (
	subtitulos => {
			sucursal => "Sucursal",
			cliente => "Cliente",
			tipo_prod => "Tipo de producto",
			plan_comer => "Plan comercial",
			item => "Item",
		},
#	data =>	%data,
);
my %arregloArchivos = ();
my %arregloSucursales = ();
my %arregloClientes = ();
my %mapa = ();
my $descIPRC;
my %datosSucursales = ();
my %datosClientes = ();

# Declaracion de variables miscelaneas
my $ext = 0;

# Declaracion de Subrutinas

# Muestra la ayuda
sub help {
	print "ListarT
	Se utiliza para resolver cualquier tipo de consulta efectuada sobre el
	parque instalado y emitir los informes correspondientes

	Modo de uso:

	\tListarT -modo tipo sucursal cliente descripcion

	\t\tmodo\tModo de ejecución de la busqueda. Se admiten los siguientes modos:

	\t\t\tc Resuelve la consulta y muestra resultados no graba en archivo.
	\t\t\te Resuelve la consulta y graba un reporte.

	\t\ttipo\tTipo de producto que sirve para determinar que archivo o archivos se van a listar.
	\t\t\tSe admite una lista de tipos separados por coma (,) o * para que se busque en todos los archivos.

	\t\tsucursal\tID de sucursal que sirve para determinar que sucursal o sucursales se van a listar.
	\t\t\t\tsucursal Una sucursal.
	\t\t\t\tNumA,NumB Rango de sucursales entre NumA y NumB.
	\t\t\t\t* Todas las sucursales.

	\t\tcliente\tID de cliente para determinar que cliente o clientes se van a listar. Se admiten los
	\t\t\tsiguientes tipos de entradas de clientes:
	\t\t\tcliente Un cliente.
	\t\t\tcliente1, cliente2 Lista de clientes separados por coma (,)
	\t\t\t* Todos los clientes.\n"
}

# Muestra el reporte y lo graba de ser requerido
sub mostrarReporte {
	if ($mostrarReporte == 1) {
		print "Reporte ListarT\n\n";
		printf("Parametros de invocacion:\n%-18s\"$ARGV[0]\"\n%-18s\"$ARGV[1]\"\n%-18s\"$ARGV[2]\"\n%-18s\"$ARGV[3]\"\n%-18s\"$ARGV[4]\"\n\n", "Modo:", "Tipo de producto:", "ID de sucursal:", "ID de cliente:", "Descripcion:");
		printf("%-30s%-50s%-20s%-50s%-20s\n", $reporte{subtitulos}{sucursal}, $reporte{subtitulos}{cliente}, $reporte{subtitulos}{tipo_prod}, $reporte{subtitulos}{plan_comer}, $reporte{subtitulos}{item});
	}
	if ($grabarReporte == 1) {
		open FILE, ">", "$repodir/lpi.$ext" or die "Error: Error inexperado al intentar crear el archivo de reporte";
		print FILE "Reporte ListarT\n\n";
		printf FILE "Parametros de invocacion:\n%-18s\"$ARGV[0]\"\n%-18s\"$ARGV[1]\"\n%-18s\"$ARGV[2]\"\n%-18s\"$ARGV[3]\"\n%-18s\"$ARGV[4]\"\n\n", "Modo:", "Tipo de producto:", "ID de sucursal:", "ID de cliente:", "Descripcion:";
		printf FILE "%-30s%-50s%-20s%-50s%-20s\n", $reporte{subtitulos}{sucursal}, $reporte{subtitulos}{cliente}, $reporte{subtitulos}{tipo_prod}, $reporte{subtitulos}{plan_comer}, $reporte{subtitulos}{item};
		close FILE;
	}
	my $totalSucursales = 0;
	my $totalClientes = 0;
	my $totalProductos = 0;
	for my $sucursal (sort keys %data) {
		for my $cliente (sort keys %{$data{$sucursal}}) {
			for my $desc (sort keys %{$data{$sucursal}{$cliente}}) {
				for my $archivo (sort keys %{$data{$sucursal}{$cliente}{$desc}}) {
						if ($mostrarReporte == 1) {
							printf("%-30s%-50s%-20s%-50s%-20s\n",
								$data{$sucursal}{$cliente}{$desc}{$archivo}{sucursal},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{cliente},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{tipo_prod},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{plan_comer},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{item});
						}
						if ($grabarReporte == 1) {
							open FILE, ">>", "$repodir/lpi.$ext" or die "Error: Error inexperado al intentar modificar el archivo de reporte";
							printf FILE "%-30s%-50s%-20s%-50s%-20s\n",
								$data{$sucursal}{$cliente}{$desc}{$archivo}{sucursal},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{cliente},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{tipo_prod},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{plan_comer},
								$data{$sucursal}{$cliente}{$desc}{$archivo}{item};
							close FILE;
						}
						$data{$sucursal}{cantidad_productos}++;
						$totalProductos++;
					}
			}
			$data{$sucursal}{cantidad_clientes}++;
			$totalClientes++;
		}
		if ($mostrarReporte == 1) {
			print "------------------------------------------------------------------------------------------------------------------------------------------------------------\n";
			printf("%-30s%-50s%-20s\n\n", "Subtotal: ", $data{$sucursal}{cantidad_clientes}, $data{$sucursal}{cantidad_productos});
		}
		if ($grabarReporte == 1) {
			open FILE, ">>", "$repodir/lpi.$ext" or die "Error: Error inexperado al intentar modificar el archivo de reporte";
			printf FILE "------------------------------------------------------------------------------------------------------------------------------------------------------------\n";
			printf FILE "%-30s%-50s%-20s\n\n", "Subtotal: ", $data{$sucursal}{cantidad_clientes}, $data{$sucursal}{cantidad_productos};
			close FILE;
		}
		$totalSucursales++;
	}
	if ($mostrarReporte == 1) {
		printf "Total:\n%-40s$totalSucursales\n%-40s$totalClientes\n%-40s$totalProductos\n", "Cantidad de sucursales procesadas: ", "Cantidad de clientes procesados: ", "Cantidad de productos procesados: ";
	}
	if ($grabarReporte == 1) {
		open FILE, ">>", "$repodir/lpi.$ext" or die "Error: Error inexperado al intentar modificar el archivo de reporte";
		printf FILE "Total:\n%-40s$totalSucursales\n%-40s$totalClientes\n%-40s$totalProductos\n", "Cantidad de sucursales procesadas: ", "Cantidad de clientes procesados: ", "Cantidad de productos procesados: ";
		close FILE;
	}
}

# Expande el rango de sucursales
sub expanderSucursales {
	if (scalar(keys %arregloSucursales) == 1){
		return;
	}
	my @arregloAux = values %arregloSucursales;
	my $ini = 0;
	my $fin = 0;
	my $arrSize = scalar(keys %arregloSucursales);
	if ($arregloAux[0] < $arregloAux[$arrSize - 1]) {
		$ini = $arregloAux[0];
		$fin = $arregloAux[$arrSize - 1];
	} else {
		$fin = $arregloAux[0];
		$ini = $arregloAux[$arrSize - 1];
	}
	%arregloSucursales = ();
	for (; $ini <= $fin; $ini++) {
		if ($datosSucursales{$ini}) {
			$arregloSucursales{$ini} = $ini;
		}
	}
}

# Carga mapa de busquedas
sub cargarMapaBusquedas {
	for my $valS (sort keys %arregloSucursales) {
		for my $valC (sort keys %arregloClientes) {
			$mapa{$valS}{$valC} = "$arregloSucursales{$valS},$arregloClientes{$valC},.*$descIPRC.*,";
		}
	}
}

# Busca ocurrencias del mapa de busquedas en la cadena pasada por parametro
sub buscar {
	my $cadena = shift;
	for my $valS (sort keys %mapa) {
		for my $valC (sort keys %{$mapa{$valS}}) {
			if ($cadena =~ m/^$mapa{$valS}{$valC}/) {
				return 1;
			}
		}
	}
	return 0;
}

# Carga los datos de los archivos maestros necesarios
sub cargarDatosMaestros {
	my @vecAux;
	
	# Cargo datos sucursales
	open (ARCHIVO, $archivoSucursales);
	while (<ARCHIVO>) {
		if("$_" ne "" ){

		@vecAux = split(",", $_);
		if (@vecAux < 8) {
			print "Error: Archivo maestro de sucursales corrupto\n";
			exit;
		}
		if (!$datosSucursales{$vecAux[2]}) {
			$datosSucursales{$vecAux[2]} = $vecAux[3];
		}
		}
	}
	close (ARCHIVO);
	
	# Cargo datos clientes
	open (ARCHIVO, $archivoClientes);
	while (<ARCHIVO>) {
		if("$_" ne "" ){

		@vecAux = split(";", $_);
		if (@vecAux < 7) {
			print "Error: Archivo maestro de clientes corrupto\n";
			exit;
		}
		if (!$datosClientes{$vecAux[0]}) {
			$datosClientes{$vecAux[0]} = $vecAux[2];
		}
}
	}
	close (ARCHIVO);
}

# Revisa que existan los archivos maestros necesarios para generar el reporte
if (!-e $archivoSucursales) {
	print "Error: El archivo de sucursales no existe\n";
	exit;
}
if (!-e $archivoClientes) {
	print "Error: El archivo de clientes no existe\n";
	exit;
}

# Valida modo
if ($modo eq "-H"){
	if (@ARGV > 1) {
		print "Error de sintaxis: Cantidad de parametros incorrecta\n";
		exit;
	}
	help;
	exit;
} elsif ($modo eq "-EC" || $modo eq "-CE" || $modo eq "-E") {
	if ($modo eq "-EC" || $modo eq "-CE") {
		$mostrarReporte = 1;
	}
	if (!(-e "$repodir")) {
		print "Error: Directorio de reportes no existente \"$repodir\"\n";
		exit;
	}
	
	# Calcula la extension del archivo a grabar
	my @tempVec;
	opendir(DIR, "$repodir");
	my @vecDir = readdir(DIR);
	foreach my $file (@vecDir) {
		if ($file =~ m/^lpi.\d+$/) {
			@tempVec = split('\.', $file);
			if (@tempVec >= 2 && $tempVec[1] =~ /^\d+$/ && int($tempVec[1]) > $ext) {
				$ext = $tempVec[1];
			}
		}
	}
	closedir(DIR);
	$ext += 1;
	$grabarReporte = 1;
} elsif ($modo eq "-C") {
	$mostrarReporte = 1;
} else {
	print "Error de sintaxis: Modo \"$ARGV[0]\" no valido\n";
	exit;
}

if (@ARGV != 5) {
	print "Error de sintaxis: Cantidad de parametros incorrecta\n";
	exit;
}

$descIPRC = $ARGV[4];

# Obtiene datos de linea de comando
for my $linea (split(',',$ARGV[1])) {
	$arregloArchivos{$linea} = $linea;
}

for my $linea (split(',',$ARGV[2])) {
	$arregloSucursales{$linea} = $linea;
}

for my $linea (split(',',$ARGV[3])) {
	$arregloClientes{$linea} = $linea;
}

# Valida los archivos ingresados
for my $valA (sort keys %arregloArchivos) {
	if ($valA eq "*") {
		if (scalar(keys %arregloArchivos) == 1) {
			%arregloArchivos = ();
			opendir(DIR, "$dirGrabarParque");
			my @vecDir = readdir(DIR);
			foreach my $file (@vecDir) {
				$arregloArchivos{$file} = $file;
			}
			closedir(DIR);
			last;
		} else {
			print "Error: Cadena de parametros de archivos no valida\n";
			exit;
		}
	}
}

if (scalar(keys %arregloSucursales) > 2) {
	print "Error: La cantidad de sucursales solamente puede ser 1, todas o un rango\n";
	exit;
}

# Valida las sucursales ingresadas
for my $valS (sort keys %arregloSucursales) {
	if ($valS eq "*") {
		if (scalar(keys %arregloSucursales) == 1) {
			%arregloSucursales = (todos => "\\d*",);
			last;
		} else {
			print "Error: Cadena de parametros de sucursales no valida\n";
			exit;
		}
	} else {
		if (!($valS =~ /^[+-]?\d+$/)) {
			print "Error: Cadena de parametros de sucursales no valida\n";
			exit;
		}
	}
}

# Valida los clientes ingresados
for my $valC (sort keys %arregloClientes) {
	if ($valC eq "*") {
		if (scalar(keys %arregloClientes) == 1) {
			%arregloClientes = (todos => "\\d*",);
			last;
		} else {
			print "Error: Cadena de parametros de clientes no valida\n";
			exit;
		}
	} else {
		if (!($valC =~ /^[+-]?\d+$/)) {
			print "Error: Cadena de parametros de clientes no valida\n";
			exit;
		}
	}
}

# Revisa si existen todos los archivos antes de procesarlos
for my $archivo (sort keys %arregloArchivos) {
	if (!(-e "$dirGrabarParque/$arregloArchivos{$archivo}")) {
		print "Error: Archivo inexistente: \"$dirGrabarParque/$arregloArchivos{$archivo}\"\n";
		exit;
	}
}

cargarDatosMaestros();
expanderSucursales();
cargarMapaBusquedas();

# Procesa todos los archivos
for my $archivo (sort keys %arregloArchivos) {
	open (ARCHIVO, "$dirGrabarParque/$arregloArchivos{$archivo}");
	while (<ARCHIVO>) {
		chomp();

		my $resultado = buscar($_);
		if ($resultado) {
			my @camposArchivo = split(",", $_);

			if (!$datosSucursales{$camposArchivo[0]}) {
				print "Error: Sucursal inexistente: \"$camposArchivo[0]\"\n";
				exit;
			}
			$data{$camposArchivo[0]}{$camposArchivo[1]}{$camposArchivo[2]}{$arregloArchivos{$archivo}}{sucursal} = "$camposArchivo[0] $datosSucursales{$camposArchivo[0]}";
			$data{$camposArchivo[0]}{$camposArchivo[1]}{$camposArchivo[2]}{$arregloArchivos{$archivo}}{cliente} = "$camposArchivo[1] $datosClientes{$camposArchivo[1]}";
			$data{$camposArchivo[0]}{$camposArchivo[1]}{$camposArchivo[2]}{$arregloArchivos{$archivo}}{tipo_prod} = "$arregloArchivos{$archivo}";
			$data{$camposArchivo[0]}{$camposArchivo[1]}{$camposArchivo[2]}{$arregloArchivos{$archivo}}{plan_comer} = "$camposArchivo[2]";
			$data{$camposArchivo[0]}{$camposArchivo[1]}{$camposArchivo[2]}{$arregloArchivos{$archivo}}{item} = "$camposArchivo[3]";
		}
	}
	close (ARCHIVO);
}

# Muestra reporte
mostrarReporte();

